package wickethub.data;

import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.NIOFSDirectory;

import wickethub.domain.Resource;

import com.google.inject.Inject;
import com.google.inject.Singleton;

@Singleton
public class Indexer {

	private static final Log log = LogFactory.getLog(Indexer.class);
	private final IndexWriter writer; // all threads share the same IndexWriter for the index process
	private final Directory directory;
	@Inject private SearcherProvider searcher;

	public Indexer() {
		try {
			directory = NIOFSDirectory.getDirectory(Util.getLuceneDirectory());
			this.writer = new IndexWriter(directory, new StandardAnalyzer(), IndexWriter.MaxFieldLength.LIMITED);
		} catch (Exception e) {
			throw new RuntimeException("FATAL: Could not open the IndexWriter", e);
		}
	}
	
    public void index(Resource r) {
		
		Document document = new Document();
		document.add(new Field("id", r.getId().toString(), Field.Store.YES, Field.Index.NOT_ANALYZED));
		document.add(new Field("text", r.getTitle(), Field.Store.NO, Field.Index.ANALYZED));
		if (r.getDescription() != null) {
			document.add(new Field("text", r.getDescription(), Field.Store.NO, Field.Index.ANALYZED));
		}
		if (r.getTopics() != null) {
			document.add(new Field("topic", r.getTopics(), Field.Store.NO, Field.Index.ANALYZED));
			// FIXME workaround until chaning queryparser for simpler queries to search on multiple fields
			document.add(new Field("text", r.getTopics(), Field.Store.NO, Field.Index.ANALYZED));
		}
		if (r.getVersions() != null) {
			document.add(new Field("version", r.getVersions(), Field.Store.NO, Field.Index.ANALYZED));
		}
		
		try {
			
			writer.updateDocument(new Term("id", r.getId().toString()), document);
			writer.commit();
			
			// our current strategy: for every resource indexed, trigger a reopen
			
			new FutureTask<Void>(new Callable<Void>() {
				public Void call() throws Exception {
					writer.optimize(); // TODO this may need to be put in another batch job
					searcher.triggerIndexReopen();
					return null; // we're actually only interested in this task's side-effects
				}
			}).run();
			
		} catch (IOException e) {
			// if there's an IOException, what can we tell the client? you haven't been indexed? don't think so
			log.error("Hey, resource " + r.getId() + " has not been indexed!", e);
		}
	
    }
    
    public void closeIndexWriter() {
    	try {
    		writer.close(true);
    	} catch (IOException ioe) {
    		try {
    			// force unlock
				IndexWriter.unlock(directory);
			} catch (IOException e) {
				// how annoying these checked exceptions can be - we give up
				throw new RuntimeException(e);
			}
    	} 
    }
	
	
}
